home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Mail / pine3.92 / pico / display.c < prev    next >
C/C++ Source or Header  |  1996-03-16  |  53KB  |  2,378 lines

  1. #if    !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: display.c,v 4.62 1996/03/16 23:33:24 mikes Exp $";
  3. #endif
  4. /*
  5.  * Program:    Display functions
  6.  *
  7.  *
  8.  * Michael Seibel
  9.  * Networks and Distributed Computing
  10.  * Computing and Communications
  11.  * University of Washington
  12.  * Administration Builiding, AG-44
  13.  * Seattle, Washington, 98195, USA
  14.  * Internet: mikes@cac.washington.edu
  15.  *
  16.  * Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  17.  *
  18.  *
  19.  * Pine and Pico are registered trademarks of the University of Washington.
  20.  * No commercial use of these trademarks may be made without prior written
  21.  * permission of the University of Washington.
  22.  * 
  23.  * Pine, Pico, and Pilot software and its included text are Copyright
  24.  * 1989-1996 by the University of Washington.
  25.  * 
  26.  * The full text of our legal notices is contained in the file called
  27.  * CPYRIGHT, included with this distribution.
  28.  *
  29.  */
  30. /*
  31.  * The functions in this file handle redisplay. There are two halves, the
  32.  * ones that update the virtual display screen, and the ones that make the
  33.  * physical display screen the same as the virtual display screen. These
  34.  * functions use hints that are left in the windows by the commands.
  35.  *
  36.  */
  37.  
  38. #include        <stdio.h>
  39. #include    "osdep.h"
  40. #include        "pico.h"
  41. #include    "estruct.h"
  42. #include        "edef.h"
  43. #include        "efunc.h"
  44.  
  45.  
  46. #ifdef    ANSI
  47.     int vtmove(int, int);
  48.     int vtputc(CELL);
  49.     int vtpute(CELL);
  50.     int vteeol(void);
  51.     int updateline(int, CELL *, CELL *, short *);
  52.     int updext(void);
  53.     int mlputi(int, int);
  54.     int mlputli(long, int);
  55.     int showCompTitle(void);
  56.     int nlforw(void);
  57.     int dumbroot(int, int);
  58.     int dumblroot(long, int);
  59. #else
  60.     int vtmove();
  61.     int vtputc();
  62.     int vtpute();
  63.     int vteeol();
  64.     int updateline();
  65.     int updext();
  66.     int mlputi();
  67.     int mlputli();
  68.     int showCompTitle();
  69.     int nlforw();
  70.     int dumbroot();
  71.     int dumblroot();
  72. #endif
  73.  
  74.  
  75. /*
  76.  * Standard pico keymenus...
  77.  */
  78. static KEYMENU menu_pico[] = {
  79.     {"^G", "Get Help", KS_SCREENHELP},    {"^O", "WriteOut", KS_SAVEFILE},
  80.     {"^R", "Read File", KS_READFILE},    {"^Y", "Prev Pg", KS_PREVPAGE},
  81.     {"^K", "Cut Text", KS_NONE},    {"^C", "Cur Pos", KS_CURPOSITION},
  82.     {"^X", "Exit", KS_EXIT},        {"^J", "Justify", KS_JUSTIFY},
  83.     {"^W", "Where is", KS_WHEREIS},    {"^V", "Next Pg", KS_NEXTPAGE},
  84.     {"^U", NULL, KS_NONE},
  85. #ifdef    SPELLER
  86.     {"^T", "To Spell", KS_SPELLCHK}
  87. #else
  88.     {"^D", "Del Char", KS_NONE}
  89. #endif
  90. };
  91. #define    UNCUT_KEY    10
  92.  
  93.  
  94. static KEYMENU menu_compose[] = {
  95.     {"^G", "Get Help", KS_SCREENHELP},    {"^X", NULL, KS_SEND},
  96.     {"^R", "Read File", KS_READFILE},    {"^Y", "Prev Pg", KS_PREVPAGE},
  97.     {"^K", "Cut Text", KS_NONE},    {"^O", "Postpone", KS_POSTPONE},
  98.     {"^C", "Cancel", KS_CANCEL},    {"^J", "Justify", KS_JUSTIFY},
  99.     {NULL, NULL, KS_NONE},        {"^V", "Next Pg", KS_NEXTPAGE},
  100.     {"^U", NULL, KS_NONE},
  101. #ifdef    SPELLER
  102.     {"^T", "To Spell", KS_SPELLCHK}
  103. #else
  104.     {"^D", "Del Char", KS_NONE}
  105. #endif
  106. };
  107. #define    EXIT_KEY    1
  108. #define    PSTPN_KEY    5
  109. #define    WHERE_KEY    8
  110.  
  111.  
  112. /*
  113.  * Definition's for pico's modeline
  114.  */
  115. #define    PICO_TITLE    "  UW PICO(tm) %s  "
  116. #define    PICO_MOD_MSG    "Modified  "
  117. #define    PICO_NEWBUF_MSG    " New Buffer "
  118.  
  119. #define WFDEBUG 0                       /* Window flag debug. */
  120.  
  121. typedef struct  VIDEO {
  122.         short   v_flag;                 /* Flags */
  123.         CELL    v_text[1];              /* Screen data. */
  124. }       VIDEO;
  125.  
  126. #define VFCHG   0x0001                  /* Changed flag            */
  127. #define    VFEXT    0x0002            /* extended (beyond column 80)    */
  128. #define    VFREV    0x0004            /* reverse video status        */
  129. #define    VFREQ    0x0008            /* reverse video request    */
  130.  
  131. int     vtrow   = 0;                    /* Row location of SW cursor */
  132. int     vtcol   = 0;                    /* Column location of SW cursor */
  133. int     ttrow   = HUGE;                 /* Row location of HW cursor */
  134. int     ttcol   = HUGE;                 /* Column location of HW cursor */
  135. int    lbound    = 0;            /* leftmost column of current line
  136.                        being displayed */
  137.  
  138. VIDEO   **vscreen;                      /* Virtual screen. */
  139. VIDEO   **pscreen;                      /* Physical screen. */
  140.  
  141.  
  142. /*
  143.  * Initialize the data structures used by the display code. The edge vectors
  144.  * used to access the screens are set up. The operating system's terminal I/O
  145.  * channel is set up. All the other things get initialized at compile time.
  146.  * The original window has "WFCHG" set, so that it will get completely
  147.  * redrawn on the first call to "update".
  148.  */
  149. vtinit()
  150. {
  151.     register int i;
  152.     register VIDEO *vp;
  153.  
  154.     (*term.t_open)();
  155.  
  156.     (*term.t_rev)(FALSE);
  157.     vscreen = (VIDEO **) malloc((term.t_nrow+1)*sizeof(VIDEO *));
  158.     if (vscreen == NULL){
  159.     emlwrite("Allocating memory for virtual display failed.", NULL);
  160.         return(FALSE);
  161.     }
  162.  
  163.  
  164.     pscreen = (VIDEO **) malloc((term.t_nrow+1)*sizeof(VIDEO *));
  165.     if (pscreen == NULL){
  166.     free((void *)vscreen);
  167.     emlwrite("Allocating memory for physical display failed.", NULL);
  168.         return(FALSE);
  169.     }
  170.  
  171.  
  172.     for (i = 0; i <= term.t_nrow; ++i) {
  173.         vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
  174.  
  175.         if (vp == NULL){
  176.         free((void *)vscreen);
  177.         free((void *)pscreen);
  178.         emlwrite("Allocating memory for virtual display lines failed.",
  179.              NULL);
  180.             return(FALSE);
  181.     }
  182.     else
  183.       memset(vp, ' ', sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
  184.  
  185.     vp->v_flag = 0;
  186.         vscreen[i] = vp;
  187.         vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
  188.  
  189.         if (vp == NULL){
  190.             free((void *)vscreen[i]);
  191.         while(--i >= 0){
  192.         free((void *)vscreen[i]);
  193.         free((void *)pscreen[i]);
  194.         }
  195.  
  196.         free((void *)vscreen);
  197.         free((void *)pscreen);
  198.         emlwrite("Allocating memory for physical display lines failed.",
  199.              NULL);
  200.             return(FALSE);
  201.     }
  202.     else
  203.       memset(vp, ' ', sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
  204.  
  205.     vp->v_flag = 0;
  206.         pscreen[i] = vp;
  207.     }
  208.  
  209.     return(TRUE);
  210. }
  211.  
  212.  
  213. /*
  214.  * Clean up the virtual terminal system, in anticipation for a return to the
  215.  * operating system. Move down to the last line and clear it out (the next
  216.  * system prompt will be written in the line). Shut down the channel to the
  217.  * terminal.
  218.  */
  219. vttidy()
  220. {
  221.     movecursor(term.t_nrow-1, 0);
  222.     peeol();
  223.     movecursor(term.t_nrow, 0);
  224.     peeol();
  225.     (*term.t_close)();
  226. }
  227.  
  228.  
  229. /*
  230.  * Set the virtual cursor to the specified row and column on the virtual
  231.  * screen. There is no checking for nonsense values; this might be a good
  232.  * idea during the early stages.
  233.  */
  234. vtmove(row, col)
  235. int row, col;
  236. {
  237.     vtrow = row;
  238.     vtcol = col;
  239. }
  240.  
  241.  
  242. /*
  243.  * Write a character to the virtual screen. The virtual row and column are
  244.  * updated. If the line is too long put a "$" in the last column. This routine
  245.  * only puts printing characters into the virtual terminal buffers. Only
  246.  * column overflow is checked.
  247.  */
  248. vtputc(c)
  249. CELL c;
  250. {
  251.     register VIDEO      *vp;
  252.     CELL     ac;
  253.  
  254.     vp = vscreen[vtrow];
  255.     ac.c = ' ';
  256.     ac.a = c.a;
  257.  
  258.     if (vtcol >= term.t_ncol) {
  259.         vtcol = (vtcol + 0x07) & ~0x07;
  260.     ac.c = '$';
  261.         vp->v_text[term.t_ncol - 1] = ac;
  262.     }
  263.     else if (c.c == '\t') {
  264.         do {
  265.             vtputc(ac);
  266.     }
  267.         while ((vtcol&0x07) != 0);
  268.     }
  269.     else if (c.c < 0x20 || c.c == 0x7F) {
  270.     ac.c = '^';
  271.         vtputc(ac);
  272.     ac.c = (c.c ^ 0x40);
  273.         vtputc(ac);
  274.     }
  275.     else
  276.       vp->v_text[vtcol++] = c;
  277. }
  278.  
  279.  
  280. /* put a character to the virtual screen in an extended line. If we are
  281.  * not yet on left edge, don't print it yet. check for overflow on
  282.  * the right margin.
  283.  */
  284. vtpute(c)
  285. CELL c;
  286. {
  287.     register VIDEO      *vp;
  288.     CELL                 ac;
  289.  
  290.     vp = vscreen[vtrow];
  291.     ac.c = ' ';
  292.     ac.a = c.a;
  293.  
  294.     if (vtcol >= term.t_ncol) {
  295.         vtcol = (vtcol + 0x07) & ~0x07;
  296.     ac.c = '$';
  297.         vp->v_text[term.t_ncol - 1] = ac;
  298.     }
  299.     else if (c.c == '\t'){
  300.         do {
  301.             vtpute(ac);
  302.         }
  303.         while (((vtcol + lbound)&0x07) != 0 && vtcol < term.t_ncol);
  304.     }
  305.     else if (c.c < 0x20 || c.c == 0x7F) {
  306.     ac.c = '^';
  307.         vtpute(ac);
  308.     ac.c = (c.c ^ 0x40);
  309.         vtpute(ac);
  310.     }
  311.     else {
  312.     if (vtcol >= 0)
  313.       vp->v_text[vtcol] = c;
  314.     ++vtcol;
  315.     }
  316. }
  317.  
  318.  
  319. /*
  320.  * Erase from the end of the software cursor to the end of the line on which
  321.  * the software cursor is located.
  322.  */
  323. vteeol()
  324. {
  325.     register VIDEO      *vp;
  326.     CELL     c;
  327.  
  328.     c.c = ' ';
  329.     c.a = 0;
  330.     vp = vscreen[vtrow];
  331.     while (vtcol < term.t_ncol)
  332.       vp->v_text[vtcol++] = c;
  333. }
  334.  
  335.  
  336. /*
  337.  * Make sure that the display is right. This is a three part process. First,
  338.  * scan through all of the windows looking for dirty ones. Check the framing,
  339.  * and refresh the screen. Second, make sure that "currow" and "curcol" are
  340.  * correct for the current window. Third, make the virtual and physical
  341.  * screens the same.
  342.  */
  343. update()
  344. {
  345.     register LINE   *lp;
  346.     register WINDOW *wp;
  347.     register VIDEO  *vp1;
  348.     register VIDEO  *vp2;
  349.     register int     i;
  350.     register int     j;
  351.     register int     scroll = 0;
  352.     CELL         c;
  353.  
  354. #if    TYPEAH
  355.     if (typahead())
  356.     return(TRUE);
  357. #endif
  358.  
  359. #ifdef _WINDOWS
  360.     /* This tells our MS Windows module to not bother updating the
  361.      * cursor position while a massive screen update is in progress.
  362.      */
  363.     mswin_beginupdate ();
  364. #endif
  365.  
  366. /*
  367.  * BUG: setting and unsetting whole region at a time is dumb.  fix this.
  368.  */
  369.     if(curwp->w_markp){
  370.     unmarkbuffer();
  371.     markregion(1);
  372.     }
  373.  
  374.     wp = wheadp;
  375.  
  376.     while (wp != NULL){
  377.         /* Look at any window with update flags set on. */
  378.  
  379.         if (wp->w_flag != 0){
  380.             /* If not force reframe, check the framing. */
  381.  
  382.             if ((wp->w_flag & WFFORCE) == 0){
  383.                 lp = wp->w_linep;
  384.  
  385.                 for (i = 0; i < wp->w_ntrows; ++i){
  386.                     if (lp == wp->w_dotp)
  387.               goto out;
  388.  
  389.                     if (lp == wp->w_bufp->b_linep)
  390.               break;
  391.  
  392.                     lp = lforw(lp);
  393.         }
  394.         }
  395.  
  396.             /* Not acceptable, better compute a new value for the line at the
  397.              * top of the window. Then set the "WFHARD" flag to force full
  398.              * redraw.
  399.              */
  400.             i = wp->w_force;
  401.  
  402.             if (i > 0){
  403.                 --i;
  404.  
  405.                 if (i >= wp->w_ntrows)
  406.                   i = wp->w_ntrows-1;
  407.         }
  408.             else if (i < 0){
  409.                 i += wp->w_ntrows;
  410.  
  411.                 if (i < 0)
  412.           i = 0;
  413.         }
  414.             else if(optimize){
  415.         /* 
  416.          * find dotp, if its been moved just above or below the 
  417.          * window, use scrollxxx() to facilitate quick redisplay...
  418.          */
  419.         lp = lforw(wp->w_dotp);
  420.         if(lp != wp->w_dotp){
  421.             if(lp == wp->w_linep && lp != wp->w_bufp->b_linep){
  422.             scroll = 1;
  423.             }
  424.             else {
  425.             lp = wp->w_linep;
  426.             for(j=0;j < wp->w_ntrows; ++j){
  427.                 if(lp != wp->w_bufp->b_linep)
  428.                   lp = lforw(lp);
  429.                 else
  430.                   break;
  431.             }
  432.             if(lp == wp->w_dotp && j == wp->w_ntrows)
  433.               scroll = 2;
  434.             }
  435.         }
  436.         j = i = wp->w_ntrows/2;
  437.         }
  438.         else
  439.           i = wp->w_ntrows/2;
  440.  
  441.             lp = wp->w_dotp;
  442.  
  443.             while (i != 0 && lback(lp) != wp->w_bufp->b_linep){
  444.                 --i;
  445.                 lp = lback(lp);
  446.         }
  447.  
  448.         /*
  449.          * this is supposed to speed things up by using tcap sequences
  450.          * to efficiently scroll the terminal screen.  the thinking here
  451.          * is that its much faster to update pscreen[] than to actually
  452.          * write the stuff to the screen...
  453.          */
  454.         if(optimize){
  455.         switch(scroll){
  456.           case 1:            /* scroll text down */
  457.             j = j-i+1;            /* add one for dot line */
  458.             /* 
  459.              * do we scroll down the header as well?  Well, only 
  460.              * if we're not editing the header, we've backed up 
  461.              * to the top, and the composer is not being 
  462.              * displayed...
  463.              */
  464.             if(Pmaster && Pmaster->headents && !ComposerEditing 
  465.                && (lback(lp) == wp->w_bufp->b_linep)
  466.                && (ComposerTopLine == COMPOSER_TOP_LINE))
  467.               j += entry_line(1000, TRUE); /* Never > 1000 headers */
  468.  
  469.             scrolldown(wp, -1, j);
  470.             break;
  471.           case 2:            /* scroll text up */
  472.             j = wp->w_ntrows - (j-i);    /* we chose new top line! */
  473.             if(Pmaster && j){
  474.             /* 
  475.              * do we scroll down the header as well?  Well, only 
  476.              * if we're not editing the header, we've backed up 
  477.              * to the top, and the composer is not being 
  478.              * displayed...
  479.              */
  480.             if(!ComposerEditing 
  481.                && (ComposerTopLine != COMPOSER_TOP_LINE))
  482.               scrollup(wp, COMPOSER_TOP_LINE, 
  483.                    j+entry_line(1000, TRUE));
  484.             else
  485.               scrollup(wp, -1, j);
  486.             }
  487.             else
  488.               scrollup(wp, -1, j);
  489.             break;
  490.             default :
  491.               break;
  492.         }
  493.         }
  494.  
  495.             wp->w_linep = lp;
  496.             wp->w_flag |= WFHARD;       /* Force full. */
  497. out:
  498.         /*
  499.          * if the line at the top of the page is the top line
  500.          * in the body, show the header...
  501.          */
  502.         if(Pmaster && Pmaster->headents && !ComposerEditing){
  503.         if(lback(wp->w_linep) == wp->w_bufp->b_linep){
  504.             if(ComposerTopLine == COMPOSER_TOP_LINE){
  505.             i = term.t_nrow - 2 - term.t_mrow - HeaderLen();
  506.             if(i > 0 && nlforw() >= i) {    /* room for header ? */
  507.                 if((i = nlforw()/2) == 0 && term.t_nrow&1)
  508.                   i = 1;
  509.                 while(wp->w_linep != wp->w_bufp->b_linep && i--)
  510.                   wp->w_linep = lforw(wp->w_linep);
  511.                 
  512.             }
  513.             else
  514.               ToggleHeader(1);
  515.             }
  516.         }
  517.         else{
  518.             if(ComposerTopLine != COMPOSER_TOP_LINE)
  519.               ToggleHeader(0);        /* hide it ! */
  520.         }
  521.         }
  522.  
  523.             /* Try to use reduced update. Mode line update has its own special
  524.              * flag. The fast update is used if the only thing to do is within
  525.              * the line editing.
  526.              */
  527.             lp = wp->w_linep;
  528.             i = wp->w_toprow;
  529.  
  530.             if ((wp->w_flag & ~WFMODE) == WFEDIT){
  531.                 while (lp != wp->w_dotp){
  532.                     ++i;
  533.                     lp = lforw(lp);
  534.         }
  535.  
  536.                 vscreen[i]->v_flag |= VFCHG;
  537.                 vtmove(i, 0);
  538.  
  539.                 for (j = 0; j < llength(lp); ++j)
  540.                     vtputc(lgetc(lp, j));
  541.  
  542.                 vteeol();
  543.         }
  544.         else if ((wp->w_flag & (WFEDIT | WFHARD)) != 0){
  545.                 while (i < wp->w_toprow+wp->w_ntrows){
  546.                     vscreen[i]->v_flag |= VFCHG;
  547.                     vtmove(i, 0);
  548.  
  549.             /* if line has been changed */
  550.                     if (lp != wp->w_bufp->b_linep){
  551.                         for (j = 0; j < llength(lp); ++j)
  552.                             vtputc(lgetc(lp, j));
  553.  
  554.                         lp = lforw(lp);
  555.             }
  556.  
  557.                     vteeol();
  558.                     ++i;
  559.         }
  560.         }
  561. #if ~WFDEBUG
  562.             if ((wp->w_flag&WFMODE) != 0)
  563.                 modeline(wp);
  564.  
  565.             wp->w_flag  = 0;
  566.             wp->w_force = 0;
  567. #endif
  568.     }
  569. #if WFDEBUG
  570.         modeline(wp);
  571.         wp->w_flag =  0;
  572.         wp->w_force = 0;
  573. #endif
  574.  
  575.     /* and onward to the next window */
  576.         wp = wp->w_wndp;
  577.     }
  578.  
  579.     /* Always recompute the row and column number of the hardware cursor. This
  580.      * is the only update for simple moves.
  581.      */
  582.     lp = curwp->w_linep;
  583.     currow = curwp->w_toprow;
  584.  
  585.     while (lp != curwp->w_dotp){
  586.         ++currow;
  587.         lp = lforw(lp);
  588.     }
  589.  
  590.     curcol = 0;
  591.     i = 0;
  592.  
  593.     while (i < curwp->w_doto){
  594.     c = lgetc(lp, i++);
  595.  
  596.         if (c.c == '\t')
  597.             curcol |= 0x07;
  598.         else if (c.c < 0x20 || c.c == 0x7F)
  599.             ++curcol;
  600.  
  601.         ++curcol;
  602.     }
  603.  
  604.     if (curcol >= term.t_ncol) {         /* extended line. */
  605.     /* flag we are extended and changed */
  606.     vscreen[currow]->v_flag |= VFEXT | VFCHG;
  607.     updext();                /* and output extended line */
  608.     } else
  609.       lbound = 0;                /* not extended line */
  610.  
  611.     /* make sure no lines need to be de-extended because the cursor is
  612.      * no longer on them 
  613.      */
  614.  
  615.     wp = wheadp;
  616.  
  617.     while (wp != NULL) {
  618.     lp = wp->w_linep;
  619.     i = wp->w_toprow;
  620.  
  621.     while (i < wp->w_toprow + wp->w_ntrows) {
  622.         if (vscreen[i]->v_flag & VFEXT) {
  623.         /* always flag extended lines as changed */
  624.         vscreen[i]->v_flag |= VFCHG;
  625.         if ((wp != curwp) || (lp != wp->w_dotp) ||
  626.             (curcol < term.t_ncol)) {
  627.             vtmove(i, 0);
  628.             for (j = 0; j < llength(lp); ++j)
  629.               vtputc(lgetc(lp, j));
  630.             vteeol();
  631.  
  632.             /* this line no longer is extended */
  633.             vscreen[i]->v_flag &= ~VFEXT;
  634.         }
  635.         }
  636.         lp = lforw(lp);
  637.         ++i;
  638.     }
  639.     /* and onward to the next window */
  640.         wp = wp->w_wndp;
  641.     }
  642.  
  643.     /* Special hacking if the screen is garbage. Clear the hardware screen,
  644.      * and update your copy to agree with it. Set all the virtual screen
  645.      * change bits, to force a full update.
  646.      */
  647.  
  648.     if (sgarbf != FALSE){
  649.     if(Pmaster){
  650.         showCompTitle();
  651.  
  652.         if(ComposerTopLine != COMPOSER_TOP_LINE){
  653.         UpdateHeader();            /* arrange things */
  654.         PaintHeader(COMPOSER_TOP_LINE, TRUE);
  655.         }
  656.  
  657.         /*
  658.          * since we're using only a portion of the screen and only 
  659.          * one buffer, only clear enough screen for the current window
  660.          * which is to say the *only* window.
  661.          */
  662.         for(i=wheadp->w_toprow;i<=term.t_nrow; i++){
  663.         movecursor(i, 0);
  664.         peeol();
  665.         vscreen[i]->v_flag |= VFCHG;
  666.         }
  667.         movecursor(wheadp->w_toprow, 0);
  668.     }
  669.     else{
  670.         for (i = 0; i < term.t_nrow-term.t_mrow; ++i){
  671.         vscreen[i]->v_flag |= VFCHG;
  672.         vp1 = pscreen[i];
  673.         c.c = ' ';
  674.         c.a = 0;
  675.         for (j = 0; j < term.t_ncol; ++j)
  676.           vp1->v_text[j] = c;
  677.         }
  678.  
  679.         movecursor(0, 0);                   /* Erase the screen. */
  680.         (*term.t_eeop)();
  681.  
  682.     }
  683.  
  684.         sgarbf = FALSE;                /* Erase-page clears */
  685.         mpresf = FALSE;                /* the message area. */
  686.  
  687.     if(Pmaster)
  688.       modeline(curwp);
  689.     else
  690.       sgarbk = TRUE;            /* fix the keyhelp as well...*/
  691.     }
  692.  
  693.     /* Make sure that the physical and virtual displays agree. Unlike before,
  694.      * the "updateline" code is only called with a line that has been updated
  695.      * for sure.
  696.      */
  697.     if(Pmaster)
  698.       i = curwp->w_toprow;
  699.     else
  700.       i = 0;
  701.  
  702.     c.c = term.t_nrow - term.t_mrow;
  703.  
  704.     for (; i < (int)c.c; ++i){
  705.  
  706.         vp1 = vscreen[i];
  707.  
  708.     /* for each line that needs to be updated, or that needs its
  709.        reverse video status changed, call the line updater    */
  710.     j = vp1->v_flag;
  711.         if (j & VFCHG){
  712.  
  713. #if    TYPEAH
  714.         if (typahead()){
  715. #ifdef _WINDOWS
  716.         mswin_endupdate ();
  717. #endif
  718.             return(TRUE);
  719.         }
  720. #endif
  721.             vp2 = pscreen[i];
  722.  
  723.             updateline(i, &vp1->v_text[0], &vp2->v_text[0], &vp1->v_flag);
  724.  
  725.     }
  726.     }
  727.  
  728.     if(Pmaster == NULL){
  729.  
  730.     if(sgarbk != FALSE){
  731.         if(term.t_mrow > 0){
  732.         movecursor(term.t_nrow-1, 0);
  733.         peeol();
  734.         movecursor(term.t_nrow, 0);
  735.         peeol();
  736.         }
  737.  
  738.         if(lastflag&CFFILL){
  739.         menu_pico[UNCUT_KEY].label = "UnJustify";
  740.         emlwrite("Can now UnJustify!", NULL);
  741.         mpresf = HUGE;    /* remove this after next keystroke! */
  742.         }
  743.         else
  744.           menu_pico[UNCUT_KEY].label = "UnCut Text";
  745.  
  746.         wkeyhelp(menu_pico);
  747.         sgarbk = FALSE;
  748.         }
  749.     }
  750.  
  751.     /* Finally, update the hardware cursor and flush out buffers. */
  752.  
  753.     movecursor(currow, curcol - lbound);
  754. #ifdef _WINDOWS
  755.     mswin_endupdate ();
  756.  
  757.     /* 
  758.      * Update the scroll bars.  This function is where curbp->b_linecnt
  759.      * is really managed.  See update_scroll.
  760.      */
  761.     update_scroll ();
  762. #endif
  763.     (*term.t_flush)();
  764. }
  765.  
  766.  
  767. /* updext - update the extended line which the cursor is currently
  768.  *        on at a column greater than the terminal width. The line
  769.  *        will be scrolled right or left to let the user see where
  770.  *        the cursor is
  771.  */
  772. updext()
  773. {
  774.     register int rcursor;        /* real cursor location */
  775.     register LINE *lp;            /* pointer to current line */
  776.     register int j;            /* index into line */
  777.  
  778.     /* calculate what column the real cursor will end up in */
  779.     rcursor = ((curcol - term.t_ncol) % term.t_scrsiz) + term.t_margin;
  780.     lbound = curcol - rcursor + 1;
  781.  
  782.     /* scan through the line outputing characters to the virtual screen
  783.      * once we reach the left edge
  784.      */
  785.     vtmove(currow, -lbound);        /* start scanning offscreen */
  786.     lp = curwp->w_dotp;            /* line to output */
  787.     for (j=0; j<llength(lp); ++j)    /* until the end-of-line */
  788.       vtpute(lgetc(lp, j));
  789.  
  790.     /* truncate the virtual line */
  791.     vteeol();
  792.  
  793.     /* and put a '$' in column 1 */
  794.     vscreen[currow]->v_text[0].c = '$';
  795.     vscreen[currow]->v_text[0].a = 0;
  796. }
  797.  
  798.  
  799. /*
  800.  * Update a single line. This does not know how to use insert or delete
  801.  * character sequences; we are using VT52 functionality. Update the physical
  802.  * row and column variables. It does try an exploit erase to end of line. The
  803.  * RAINBOW version of this routine uses fast video.
  804.  */
  805. updateline(row, vline, pline, flags)
  806. int  row;
  807. CELL vline[];                /* what we want it to end up as */
  808. CELL pline[];                /* what it looks like now       */
  809. short *flags;                /* and how we want it that way  */
  810. {
  811.     register CELL *cp1;
  812.     register CELL *cp2;
  813.     register CELL *cp3;
  814.     register CELL *cp4;
  815.     register CELL *cp5;
  816.     register CELL *cp6;
  817.     register CELL *cp7;
  818.     register int  display = TRUE;
  819.     register int nbflag;        /* non-blanks to the right flag? */
  820.  
  821.  
  822.     /* set up pointers to virtual and physical lines */
  823.     cp1 = &vline[0];
  824.     cp2 = &pline[0];
  825.     cp3 = &vline[term.t_ncol];
  826.  
  827.     /* advance past any common chars at the left */
  828.     while (cp1 != cp3 && cp1[0].c == cp2[0].c && cp1[0].a == cp2[0].a) {
  829.     ++cp1;
  830.     ++cp2;
  831.     }
  832.  
  833. /* This can still happen, even though we only call this routine on changed
  834.  * lines. A hard update is always done when a line splits, a massive
  835.  * change is done, or a buffer is displayed twice. This optimizes out most
  836.  * of the excess updating. A lot of computes are used, but these tend to
  837.  * be hard operations that do a lot of update, so I don't really care.
  838.  */
  839.     /* if both lines are the same, no update needs to be done */
  840.     if (cp1 == cp3){
  841.     *flags &= ~VFCHG;            /* mark it clean */
  842.     return(TRUE);
  843.     }
  844.  
  845.     /* find out if there is a match on the right */
  846.     nbflag = FALSE;
  847.     cp3 = &vline[term.t_ncol];
  848.     cp4 = &pline[term.t_ncol];
  849.  
  850.     while (cp3[-1].c == cp4[-1].c && cp3[-1].a == cp4[-1].a) {
  851.     --cp3;
  852.     --cp4;
  853.     if (cp3[0].c != ' ' || cp3[0].a != 0)/* Note if any nonblank */
  854.       nbflag = TRUE;        /* in right match. */
  855.     }
  856.  
  857.     cp5 = cp3;
  858.  
  859.     if (nbflag == FALSE && eolexist == TRUE) {    /* Erase to EOL ? */
  860.     while (cp5 != cp1 && cp5[-1].c == ' ' && cp5[-1].a == 0)
  861.       --cp5;
  862.  
  863.     if (cp3-cp5 <= 3)        /* Use only if erase is */
  864.       cp5 = cp3;            /* fewer characters. */
  865.     }
  866.  
  867.     movecursor(row, cp1-&vline[0]);        /* Go to start of line. */
  868.  
  869.     if (!nbflag) {                /* use insert or del char? */
  870.     cp6 = cp3;
  871.     cp7 = cp4;
  872.  
  873.     if(inschar&&(cp7!=cp2 && cp6[0].c==cp7[-1].c && cp6[0].a==cp7[-1].a)){
  874.         while (cp7 != cp2 && cp6[0].c==cp7[-1].c && cp6[0].a==cp7[-1].a){
  875.         --cp7;
  876.         --cp6;
  877.         }
  878.  
  879.         if (cp7==cp2 && cp4-cp2 > 3){
  880.         o_insert((char)cp1->c);     /* insert the char */
  881.         display = FALSE;        /* only do it once!! */
  882.         }
  883.     }
  884.     else if(delchar && cp3 != cp1 && cp7[0].c == cp6[-1].c
  885.         && cp7[0].a == cp6[-1].a){
  886.         while (cp6 != cp1 && cp7[0].c==cp6[-1].c && cp7[0].a==cp6[-1].a){
  887.         --cp7;
  888.         --cp6;
  889.         }
  890.  
  891.         if (cp6==cp1 && cp5-cp6 > 3){
  892.         o_delete();        /* insert the char */
  893.         display = FALSE;        /* only do it once!! */
  894.         }
  895.     }
  896.     }
  897.  
  898.     while (cp1 != cp5) {        /* Ordinary. */
  899.     if(display){
  900.         (*term.t_rev)(cp1->a);    /* set inverse for this char */
  901.         (*term.t_putchar)(cp1->c);
  902.     }
  903.  
  904.     ++ttcol;
  905.     *cp2++ = *cp1++;
  906.     }
  907.  
  908.     (*term.t_rev)(0);            /* turn off inverse anyway! */
  909.  
  910.     if (cp5 != cp3) {            /* Erase. */
  911.     if(display)
  912.       peeol();
  913.     while (cp1 != cp3)
  914.       *cp2++ = *cp1++;
  915.     }
  916.     *flags &= ~VFCHG;            /* flag this line is changed */
  917. }
  918.  
  919.  
  920. /*
  921.  * Redisplay the mode line for the window pointed to by the "wp". This is the
  922.  * only routine that has any idea of how the modeline is formatted. You can
  923.  * change the modeline format by hacking at this routine. Called by "update"
  924.  * any time there is a dirty window.
  925.  */
  926. modeline(wp)
  927. WINDOW *wp;
  928. {
  929.     if(Pmaster){
  930.         if(ComposerEditing)
  931.       ShowPrompt();
  932.     else{
  933.         menu_compose[EXIT_KEY].label  = (Pmaster->headents)
  934.                           ? "Send" :"Exit";
  935.         menu_compose[PSTPN_KEY].name  = (Pmaster->headents)
  936.                           ? "^O" : NULL;
  937.         menu_compose[PSTPN_KEY].label = (Pmaster->headents)
  938.                           ? "Postpone" : NULL;
  939.         menu_compose[WHERE_KEY].name  = (Pmaster->alt_ed) ? "^_" : "^W";
  940.         menu_compose[WHERE_KEY].label = (Pmaster->alt_ed) ? "Alt Edit" 
  941.                                   : "Where is";
  942.         KS_OSDATASET(&menu_compose[WHERE_KEY],
  943.              (Pmaster->alt_ed) ? KS_ALTEDITOR : KS_WHEREIS);
  944.         menu_compose[UNCUT_KEY].label = (thisflag&CFFILL) ? "UnJustify"
  945.                                   : "UnCut Text";
  946.         wkeyhelp(menu_compose);
  947. #ifdef _WINDOWS
  948.         /* When alt editor is available "Where is" is not on the menu
  949.          * but the command is still available.  This call enables any
  950.          * "Where is" menu items. */
  951.         if (Pmaster->alt_ed)
  952.         mswin_menuitemadd (CTRL|'W', "", KS_WHEREIS, 0);
  953. #endif 
  954.     }
  955.     }
  956.     else{
  957.     register char *cp;
  958.     register int n;        /* cursor position count */
  959.     register BUFFER *bp;
  960.     register i;        /* loop index */
  961.     register lchar;        /* character to draw line in buffer with */
  962.     char     tline[NLINE];    /* buffer for part of mode line */
  963.     CELL     c;
  964.  
  965.     n = 0;
  966.     c.a = 1;
  967.     vtmove(1, 0);
  968.     vteeol();
  969.     vscreen[n]->v_flag |= VFCHG; /* Redraw next time. */
  970.     vtmove(n, 0);        /* Seek to right line. */
  971.  
  972. #if    REVSTA
  973.     if (revexist)
  974.       lchar = ' ';
  975.     else
  976. #endif
  977.       lchar = '-';
  978.  
  979.     c.c = lchar;
  980.     vtputc(c);
  981.     bp = wp->w_bufp;
  982.  
  983.     n = 1;
  984.  
  985.     sprintf(cp = tline, PICO_TITLE, version);    /* write version */
  986.  
  987.     while(c.c = *cp++){
  988.         vtputc(c);
  989.         ++n;
  990.     }
  991.  
  992.     if(bp->b_fname[0]){                /* File name? */
  993.         char *p, *endp, *prefix;
  994.  
  995.         prefix = NULL;                /* for abreviation */
  996.         endp   = strchr(cp = bp->b_fname, '\0');    /* find eol */
  997.         i       = term.t_ncol - n - 22;        /* space available */
  998.         while(i > 0 && endp - cp > i){
  999.         if(!prefix){
  1000.             prefix = ".../";            /* path won't fit! */
  1001.             i -= 4;
  1002.         }
  1003.  
  1004.         if(!(p = strchr(cp, '/'))){        /* neat break? */
  1005.             cp = endp - i;            /* do best we can */
  1006.             break;
  1007.         }
  1008.         else
  1009.           cp = p + 1;
  1010.         }
  1011.  
  1012.         sprintf(tline, "%*.sFile: %s%s",
  1013.             ((i - (endp - cp)) > 0) ? (i - (endp - cp))/2 : 0, " ",
  1014.             prefix ? prefix : "", cp);
  1015.  
  1016.         for(cp = tline; c.c = *cp; cp++, n++)
  1017.           vtputc(c);
  1018.         }
  1019.         else{
  1020.         cp = PICO_NEWBUF_MSG;
  1021.         if(sizeof(PICO_NEWBUF_MSG) < term.t_ncol){    /* enough room? */
  1022.         c.c = lchar;
  1023.         for(i = (term.t_ncol-sizeof(PICO_NEWBUF_MSG))/2; n < i; n++)
  1024.           vtputc(c);
  1025.         }
  1026.  
  1027.         while(c.c = *cp++){
  1028.         vtputc(c);
  1029.         ++n;
  1030.         }
  1031.     }
  1032.  
  1033. #if WFDEBUG
  1034.     vtputc(lchar);
  1035.     vtputc((wp->w_flag&WFMODE)!=0  ? 'M' : lchar);
  1036.     vtputc((wp->w_flag&WFHARD)!=0  ? 'H' : lchar);
  1037.     vtputc((wp->w_flag&WFEDIT)!=0  ? 'E' : lchar);
  1038.     vtputc((wp->w_flag&WFMOVE)!=0  ? 'V' : lchar);
  1039.     vtputc((wp->w_flag&WFFORCE)!=0 ? 'F' : lchar);
  1040.     n += 6;
  1041. #endif
  1042.     i = term.t_ncol - n;                /* space available */
  1043.     cp = NULL;
  1044.     if(bp->b_flag&BFCHG){                /* "MOD" if changed. */
  1045.         cp = PICO_MOD_MSG;
  1046.         i  = (i > sizeof(PICO_MOD_MSG)) ? i - sizeof(PICO_MOD_MSG) : 0;
  1047.     }
  1048.  
  1049.     c.c = lchar;
  1050.     while(i-- > 0)                    /* Pad width */
  1051.       vtputc(c);
  1052.  
  1053.     if(cp)
  1054.       while(c.c = *cp++)
  1055.         vtputc(c);
  1056.     }
  1057. }
  1058.  
  1059.  
  1060.  
  1061. /*
  1062.  * Send a command to the terminal to move the hardware cursor to row "row"
  1063.  * and column "col". The row and column arguments are origin 0. Optimize out
  1064.  * random calls. Update "ttrow" and "ttcol".
  1065.  */
  1066. movecursor(row, col)
  1067. int row, col;
  1068. {
  1069.     if (row!=ttrow || col!=ttcol) {
  1070.         ttrow = row;
  1071.         ttcol = col;
  1072.         (*term.t_move)(row, col);
  1073.     }
  1074. }
  1075.  
  1076.  
  1077. /*
  1078.  * Erase any sense we have of the cursor's HW location...
  1079.  */
  1080. clearcursor()
  1081. {
  1082.     ttrow = ttcol = HUGE;
  1083. }
  1084.  
  1085.  
  1086. /*
  1087.  * Erase the message line. This is a special routine because the message line
  1088.  * is not considered to be part of the virtual screen. It always works
  1089.  * immediately; the terminal buffer is flushed via a call to the flusher.
  1090.  */
  1091. mlerase()
  1092. {
  1093.     movecursor(term.t_nrow - term.t_mrow, 0);
  1094.     (*term.t_rev)(0);
  1095.     if (eolexist == TRUE)
  1096.       peeol();
  1097.     else
  1098.       while(++ttcol < term.t_ncol)        /* track's ttcol */
  1099.     (*term.t_putchar)(' ');
  1100.  
  1101.     (*term.t_flush)();
  1102.     mpresf = FALSE;
  1103. }
  1104.  
  1105.  
  1106. /*
  1107.  * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
  1108.  * ABORT. The ABORT status is returned if the user bumps out of the question
  1109.  * with a ^G. if d >= 0, d is the default answer returned. Otherwise there
  1110.  * is no default.
  1111.  */
  1112. mlyesno(prompt, dflt)
  1113. char  *prompt;
  1114. int   dflt;
  1115. {
  1116.     int     rv;
  1117.     char    buf[NLINE];
  1118.     KEYMENU menu_yesno[12];
  1119.  
  1120. #ifdef _WINDOWS
  1121.     if (mswin_usedialog ()) 
  1122.       switch (mswin_yesno (prompt)) {
  1123.     default:
  1124.     case 0:        return (ABORT);
  1125.     case 1:        return (TRUE);
  1126.     case 2:        return (FALSE);
  1127.       }
  1128. #endif  
  1129.  
  1130.     for(rv = 0; rv < 12; rv++){
  1131.     menu_yesno[rv].name = NULL;
  1132.     KS_OSDATASET(&menu_yesno[rv], KS_NONE);
  1133.     }
  1134.  
  1135.     menu_yesno[1].name  = "Y";
  1136.     menu_yesno[1].label = (dflt == TRUE) ? "[Yes]" : "Yes";
  1137.     menu_yesno[6].name  = "^C";
  1138.     menu_yesno[6].label = "Cancel";
  1139.     menu_yesno[7].name  = "N";
  1140.     menu_yesno[7].label = (dflt == FALSE) ? "[No]" : "No";
  1141.     wkeyhelp(menu_yesno);        /* paint generic menu */
  1142.     sgarbk = TRUE;            /* mark menu dirty */
  1143.     if(Pmaster)
  1144.       curwp->w_flag |= WFMODE;
  1145.  
  1146.     sprintf(buf, "%s ? ", prompt);
  1147.     mlwrite(buf, NULL);
  1148.     (*term.t_rev)(1);
  1149.     rv = -1;
  1150.     while(1){
  1151.     switch(GetKey()){
  1152.       case (CTRL|'M') :        /* default */
  1153.         if(dflt >= 0){
  1154.         pputs((dflt) ? "Yes" : "No", 1);
  1155.         rv = dflt;
  1156.         }
  1157.         else
  1158.           (*term.t_beep)();
  1159.  
  1160.         break;
  1161.  
  1162.       case (CTRL|'C') :        /* Bail out! */
  1163.       case F2         :
  1164.         pputs("ABORT", 1);
  1165.         rv = ABORT;
  1166.         break;
  1167.  
  1168.       case 'y' :
  1169.       case 'Y' :
  1170.       case F3  :
  1171.         pputs("Yes", 1);
  1172.         rv = TRUE;
  1173.         break;
  1174.  
  1175.       case 'n' :
  1176.       case 'N' :
  1177.       case F4  :
  1178.         pputs("No", 1);
  1179.         rv = FALSE;
  1180.         break;
  1181.  
  1182.       case (CTRL|'G') :
  1183.         if(term.t_mrow == 0 && km_popped == 0){
  1184.         movecursor(term.t_nrow-2, 0);
  1185.         peeol();
  1186.         term.t_mrow = 2;
  1187.         (*term.t_rev)(0);
  1188.         wkeyhelp(menu_yesno);        /* paint generic menu */
  1189.         mlwrite(buf, NULL);
  1190.         (*term.t_rev)(1);
  1191.         sgarbk = TRUE;            /* mark menu dirty */
  1192.         km_popped++;
  1193.         break;
  1194.         }
  1195.         /* else fall through */
  1196.  
  1197.       default:
  1198.         (*term.t_beep)();
  1199.       case NODATA :
  1200.         break;
  1201.     }
  1202.  
  1203.     (*term.t_flush)();
  1204.     if(rv != -1){
  1205.         (*term.t_rev)(0);
  1206.         if(km_popped){
  1207.         term.t_mrow = 0;
  1208.         movecursor(term.t_nrow, 0);
  1209.         peeol();
  1210.         sgarbf = 1;
  1211.         km_popped = 0;
  1212.         }
  1213.  
  1214.         return(rv);
  1215.     }
  1216.     }
  1217. }
  1218.  
  1219.  
  1220.  
  1221. /*
  1222.  * Write a prompt into the message line, then read back a response. Keep
  1223.  * track of the physical position of the cursor. If we are in a keyboard
  1224.  * macro throw the prompt away, and return the remembered response. This
  1225.  * lets macros run at full speed. The reply is always terminated by a carriage
  1226.  * return. Handle erase, kill, and abort keys.
  1227.  */
  1228. mlreply(prompt, buf, nbuf, flg, extras)
  1229. char      *prompt, *buf;
  1230. int       nbuf, flg;
  1231. EXTRAKEYS *extras;
  1232. {
  1233.     return(mlreplyd(prompt, buf, nbuf, flg|QDEFLT, extras));
  1234. }
  1235.  
  1236.  
  1237. /*
  1238.  * function key mappings
  1239.  */
  1240. static int rfkm[12][2] = {
  1241.     { F1,  (CTRL|'G')},
  1242.     { F2,  (CTRL|'C')},
  1243.     { F3,  0 },
  1244.     { F4,  0 },
  1245.     { F5,  0 },
  1246.     { F6,  0 },
  1247.     { F7,  0 },
  1248.     { F8,  0 },
  1249.     { F9,  0 },
  1250.     { F10, 0 },
  1251.     { F11, 0 },
  1252.     { F12, 0 }
  1253. };
  1254.  
  1255.  
  1256. /*
  1257.  * mlreplyd - write the prompt to the message line along with an default
  1258.  *          answer already typed in.  Carraige return accepts the
  1259.  *          default.  answer returned in buf which also holds the initial
  1260.  *            default, nbuf is it's length, def set means use default value,
  1261.  *            and ff means for-file which checks that all chars are allowed
  1262.  *            in file names.
  1263.  */
  1264. mlreplyd(prompt, buf, nbuf, flg, extras)
  1265. char      *prompt;
  1266. char      *buf;
  1267. int       nbuf, flg;
  1268. EXTRAKEYS *extras;
  1269. {
  1270.     register int    c;                /* current char       */
  1271.     register char   *b;                /* pointer in buf     */
  1272.     register int    i, j;
  1273.     register int    maxl;
  1274.     register int    plen;
  1275.     int      changed = FALSE;
  1276.     int      return_val = 0;
  1277.     KEYMENU  menu_mlreply[12];
  1278.     int         extra_v[12];
  1279.  
  1280. #ifdef _WINDOWS
  1281.     if (mswin_usedialog ()) {
  1282.     MDlgButton        btn_list[12];
  1283.     int            i, j;
  1284.  
  1285.     memset (&btn_list, 0, sizeof (MDlgButton) * 12);
  1286.     j = 0;
  1287.     for (i = 0; extras && extras[i].name != NULL; ++i) {
  1288.         if (extras[i].label[0] != '\0') {
  1289.         if ((extras[i].key & CTRL) == CTRL) 
  1290.             btn_list[j].ch = (extras[i].key & ~CTRL) - '@';
  1291.         else
  1292.             btn_list[j].ch = extras[i].key;
  1293.         btn_list[j].rval = extras[i].key;
  1294.         btn_list[j].name = extras[i].name;
  1295.         btn_list[j++].label = extras[i].label;
  1296.         }
  1297.     }
  1298.     btn_list[j].ch = -1;
  1299.  
  1300.     return (mswin_dialog (prompt, buf, nbuf, ((flg&QDEFLT) > 0), 
  1301.             FALSE, btn_list, NULL, 0));
  1302.     }
  1303. #endif
  1304.  
  1305.     menu_mlreply[0].name = "^G";
  1306.     menu_mlreply[0].label = "Get Help";
  1307.     KS_OSDATASET(&menu_mlreply[0], KS_SCREENHELP);
  1308.     for(j = 0, i = 1; i < 6; i++){    /* insert odd extras */
  1309.     menu_mlreply[i].name = NULL;
  1310.     KS_OSDATASET(&menu_mlreply[i], KS_NONE);
  1311.     rfkm[2*i][1] = 0;
  1312.     if(extras){
  1313.         for(; extras[j].name && j != 2*(i-1); j++)
  1314.           ;
  1315.  
  1316.         if(extras[j].name){
  1317.         rfkm[2*i][1]          = extras[j].key;
  1318.         menu_mlreply[i].name  = extras[j].name;
  1319.         menu_mlreply[i].label = extras[j].label;
  1320.         KS_OSDATASET(&menu_mlreply[i], KS_OSDATAGET(&extras[j]));
  1321.         }
  1322.     }
  1323.     }
  1324.  
  1325.     menu_mlreply[6].name = "^C";
  1326.     menu_mlreply[6].label = "Cancel";
  1327.     KS_OSDATASET(&menu_mlreply[6], KS_NONE);
  1328.     for(j = 0, i = 7; i < 12; i++){    /* insert even extras */
  1329.     menu_mlreply[i].name = NULL;
  1330.     rfkm[2*(i-6)+1][1] = 0;
  1331.     if(extras){
  1332.         for(; extras[j].name && j != (2*(i-6)) - 1; j++)
  1333.           ;
  1334.  
  1335.         if(extras[j].name){
  1336.         rfkm[2*(i-6)+1][1]    = extras[j].key;
  1337.         menu_mlreply[i].name  = extras[j].name;
  1338.         menu_mlreply[i].label = extras[j].label;
  1339.         KS_OSDATASET(&menu_mlreply[i], KS_OSDATAGET(&extras[j]));
  1340.         }
  1341.     }
  1342.     }
  1343.  
  1344.     /* set up what to watch for and return values */
  1345.     memset(extra_v, 0, sizeof(extra_v));
  1346.     for(i = 0, j = 0; i < 12 && extras && extras[i].name; i++)
  1347.       extra_v[j++] = extras[i].key;
  1348.  
  1349.     mlwrite(prompt, NULL);        /* paint prompt */
  1350.     plen = strlen(prompt);
  1351.     if(!(flg&QDEFLT))
  1352.       *buf = '\0';
  1353.  
  1354.     (*term.t_rev)(1);
  1355.  
  1356.     maxl = (nbuf < term.t_ncol - plen - 1) ? nbuf : term.t_ncol - plen - 1;
  1357.  
  1358.     pputs(buf, 1);
  1359.     b = &buf[(flg & QBOBUF) ? 0 : strlen(buf)];
  1360.     
  1361.     (*term.t_rev)(0);
  1362.     wkeyhelp(menu_mlreply);        /* paint generic menu */
  1363.     sgarbk = 1;                /* mark menu dirty */
  1364.     (*term.t_rev)(1);
  1365.  
  1366.     for (;;) {
  1367.     movecursor(term.t_nrow - term.t_mrow, plen + b - buf);
  1368.     (*term.t_flush)();
  1369.  
  1370.  
  1371.     while((c = GetKey()) == NODATA)
  1372.       ;
  1373.  
  1374.     switch(c = normalize_cmd(c, rfkm, 1)){
  1375.       case (CTRL|'A') :            /* CTRL-A beginning     */
  1376.         b = buf;
  1377.         continue;
  1378.  
  1379.       case (CTRL|'B') :            /* CTRL-B back a char   */
  1380.         if(ttcol > plen)
  1381.         b--;
  1382.         continue;
  1383.  
  1384.       case (CTRL|'C') :            /* CTRL-C abort        */
  1385.         pputs("ABORT", 1);
  1386.         ctrlg(FALSE, 0);
  1387.         (*term.t_rev)(0);
  1388.         (*term.t_flush)();
  1389.         return_val = ABORT;
  1390.         goto ret;
  1391.  
  1392.       case (CTRL|'E') :            /* CTRL-E end of line   */
  1393.         b = &buf[strlen(buf)];
  1394.         continue;
  1395.  
  1396.       case (CTRL|'F') :            /* CTRL-F forward a char*/
  1397.         if(*b != '\0')
  1398.         b++;
  1399.         continue;
  1400.  
  1401.       case (CTRL|'G') :            /* CTRL-G help        */
  1402.         if(term.t_mrow == 0 && km_popped == 0){
  1403.         movecursor(term.t_nrow-2, 0);
  1404.         peeol();
  1405.         sgarbk = 1;            /* mark menu dirty */
  1406.         km_popped++;
  1407.         term.t_mrow = 2;
  1408.         (*term.t_rev)(0);
  1409.         wkeyhelp(menu_mlreply);        /* paint generic menu */
  1410.         mlwrite(prompt, NULL);        /* paint prompt */
  1411.         plen = strlen(prompt);
  1412.         (*term.t_rev)(1);
  1413.         pputs(buf, 1);
  1414.         break;
  1415.         }
  1416.  
  1417.         pputs("HELP", 1);
  1418.         (*term.t_rev)(0);
  1419.         (*term.t_flush)();
  1420.         return_val = HELPCH;
  1421.         goto ret;
  1422.  
  1423.       case (CTRL|'H') :            /* CTRL-H backspace    */
  1424.       case 0x7f :                /*        rubout    */
  1425.         if (b <= buf)
  1426.           break;
  1427.         b--;
  1428.         ttcol--;                /* cheating!  no pputc */
  1429.         (*term.t_putchar)('\b');
  1430.  
  1431.       case (CTRL|'D') :            /* CTRL-D delete char   */
  1432.         changed=TRUE;
  1433.         i = 0;
  1434.         do                    /* blat out left char   */
  1435.           b[i] = b[i+1];
  1436.         while(b[i++] != '\0');
  1437.         break;
  1438.  
  1439.       case (CTRL|'L') :            /* CTRL-L redraw    */
  1440.         (*term.t_rev)(0);
  1441.         return_val = (CTRL|'L');
  1442.         goto ret;
  1443.  
  1444.       case (CTRL|'K') :            /* CTRL-K kill line    */
  1445.         changed=TRUE;
  1446.         buf[0] = '\0';
  1447.         b = buf;
  1448.         movecursor(ttrow, plen);
  1449.         break;
  1450.  
  1451.       case K_PAD_LEFT:
  1452.         if(ttcol > plen)
  1453.           b--;
  1454.         continue;
  1455.  
  1456.       case K_PAD_RIGHT:
  1457.         if(*b != '\0')
  1458.           b++;
  1459.         continue;
  1460.  
  1461.       case F1 :                /* sort of same thing */
  1462.         (*term.t_rev)(0);
  1463.         (*term.t_flush)();
  1464.         return_val = HELPCH;
  1465.         goto ret;
  1466.  
  1467.       case (CTRL|'M') :            /*        newline       */
  1468.         (*term.t_rev)(0);
  1469.         (*term.t_flush)();
  1470.         return_val = changed;
  1471.         goto ret;
  1472.  
  1473.       default : 
  1474.         if(strlen(buf) >= maxl){        /* contain the text      */
  1475.         (*term.t_beep)();
  1476.         continue;
  1477.         }
  1478.  
  1479.         /* look for match in extra_v */
  1480.         for(i = 0; i < 12; i++)
  1481.           if(c && c == extra_v[i]){
  1482.           (*term.t_rev)(0);
  1483.           return_val = c;
  1484.           goto ret;
  1485.           }
  1486.  
  1487.         changed=TRUE;
  1488.  
  1489.         if(c&(~0xff)){            /* bag ctrl/special chars */
  1490.         (*term.t_beep)();
  1491.         }
  1492.         else{
  1493.         i = strlen(b);
  1494.         if(flg&QFFILE){
  1495.             if(!fallowc(c)){         /* c OK in filename? */
  1496.             (*term.t_beep)();
  1497.             continue;
  1498.             }
  1499.         }
  1500.         do                /* blat out left char   */
  1501.           b[i+1] = b[i];
  1502.         while(i-- >= 0);
  1503.         *b++ = c;
  1504.         pputc(c, 0);
  1505.         }
  1506.     }
  1507.  
  1508.     pputs(b, 1);                /* show default */
  1509.     i = term.t_ncol-1;
  1510.     while(pscreen[ttrow]->v_text[i].c == ' ' 
  1511.           && pscreen[ttrow]->v_text[i].a == 0)
  1512.       i--;
  1513.  
  1514.     while(ttcol <= i)
  1515.       pputc(' ', 0);
  1516.     }
  1517.  
  1518. ret:
  1519.     if(km_popped){
  1520.     term.t_mrow = 0;
  1521.     movecursor(term.t_nrow, 0);
  1522.     peeol();
  1523.     sgarbf = 1;
  1524.     km_popped = 0;
  1525.     }
  1526.  
  1527.     return(return_val);
  1528. }
  1529.  
  1530.  
  1531. /*
  1532.  * emlwrite() - write the message string to the error half of the screen
  1533.  *              center justified.  much like mlwrite (which is still used
  1534.  *              to paint the line for prompts and such), except it center
  1535.  *              the text.
  1536.  */
  1537. void
  1538. emlwrite(message, arg) 
  1539. char    *message;
  1540. void    *arg;
  1541. {
  1542.     register char *bufp = message;
  1543.     register char *ap;
  1544.     register long l;
  1545.  
  1546.     mlerase();
  1547.  
  1548.     if((l = strlen(message)) == 0)        /* nothing to write, bag it */
  1549.       return;
  1550.  
  1551.     /*
  1552.      * next, figure out where the to move the cursor so the message 
  1553.      * comes out centered
  1554.      */
  1555.     if((ap=(char *)strchr(message, '%')) != NULL){
  1556.     l -= 2;
  1557.     switch(ap[1]){
  1558.       case '%':
  1559.       case 'c':
  1560.         l += 1;
  1561.         break;
  1562.       case 'd':
  1563.         l += (long)dumbroot((int)arg, 10);
  1564.         break;
  1565.       case 'D':
  1566.         l += (long)dumblroot((long)arg, 10);
  1567.         break;
  1568.       case 'o':
  1569.         l += (long)dumbroot((int)arg, 8);
  1570.         break;
  1571.       case 'x':
  1572.         l += (long)dumbroot((int)arg, 16);
  1573.         break;
  1574.       case 's':
  1575.             l += strlen((char *)arg);
  1576.         break;
  1577.     }
  1578.     }
  1579.  
  1580.     if(l-4 <= term.t_ncol)            /* this wouldn't be good */
  1581.       movecursor(term.t_nrow-term.t_mrow, (term.t_ncol - (int)l - 4)/2);
  1582.     else
  1583.       movecursor(term.t_nrow-term.t_mrow, 0);
  1584.  
  1585.     (*term.t_rev)(1);
  1586.     pputs("[ ", 1);
  1587.     while (*bufp != '\0' && ttcol < term.t_ncol-2){
  1588.     if(*bufp == '\007')
  1589.       (*term.t_beep)();
  1590.     else if(*bufp == '%'){
  1591.         switch(*++bufp){
  1592.           case 'c':
  1593.         pputc((char)(int)arg, 0);
  1594.         break;
  1595.           case 'd':
  1596.         mlputi((int)arg, 10);
  1597.         break;
  1598.           case 'D':
  1599.         mlputli((long)arg, 10);
  1600.         break;
  1601.           case 'o':
  1602.         mlputi((int)arg, 16);
  1603.         break;
  1604.           case 'x':
  1605.         mlputi((int)arg, 8);
  1606.         break;
  1607.           case 's':
  1608.         pputs((char *)arg, 0);
  1609.         break;
  1610.           case '%':
  1611.           default:
  1612.         pputc(*bufp, 0);
  1613.         break;
  1614.         }
  1615.     }
  1616.     else
  1617.       pputc(*bufp, 0);
  1618.     bufp++;
  1619.     }
  1620.  
  1621.     pputs(" ]", 1);
  1622.     (*term.t_rev)(0);
  1623.     (*term.t_flush)();
  1624.     mpresf = TRUE;
  1625. }
  1626.  
  1627.  
  1628. /*
  1629.  * Write a message into the message line. Keep track of the physical cursor
  1630.  * position. A small class of printf like format items is handled. Assumes the
  1631.  * stack grows down; this assumption is made by the "++" in the argument scan
  1632.  * loop. Set the "message line" flag TRUE.
  1633.  */
  1634. mlwrite(fmt, arg)
  1635. char *fmt;
  1636. void *arg;
  1637. {
  1638.     register int c;
  1639.     register char *ap;
  1640.  
  1641.     /*
  1642.      * the idea is to only highlight if there is something to show
  1643.      */
  1644.     mlerase();
  1645.  
  1646.     ttcol = 0;
  1647.     (*term.t_rev)(1);
  1648.     ap = (char *) &arg;
  1649.     while ((c = *fmt++) != 0) {
  1650.         if (c != '%') {
  1651.             (*term.t_putchar)(c);
  1652.             ++ttcol;
  1653.     }
  1654.         else {
  1655.             c = *fmt++;
  1656.             switch (c) {
  1657.           case 'd':
  1658.         mlputi(*(int *)ap, 10);
  1659.         ap += sizeof(int);
  1660.         break;
  1661.  
  1662.           case 'o':
  1663.         mlputi(*(int *)ap,  8);
  1664.         ap += sizeof(int);
  1665.         break;
  1666.  
  1667.           case 'x':
  1668.         mlputi(*(int *)ap, 16);
  1669.         ap += sizeof(int);
  1670.         break;
  1671.  
  1672.           case 'D':
  1673.         mlputli(*(long *)ap, 10);
  1674.         ap += sizeof(long);
  1675.         break;
  1676.  
  1677.           case 's':
  1678.         pputs(*(char **)ap, 1);
  1679.         ap += sizeof(char *);
  1680.         break;
  1681.  
  1682.               default:
  1683.         (*term.t_putchar)(c);
  1684.         ++ttcol;
  1685.         }
  1686.     }
  1687.     }
  1688.  
  1689.     c = ttcol;
  1690.     while(ttcol < term.t_ncol)
  1691.       pputc(' ', 0);
  1692.  
  1693.     movecursor(term.t_nrow - term.t_mrow, c);
  1694.     (*term.t_rev)(0);
  1695.     (*term.t_flush)();
  1696.     mpresf = TRUE;
  1697. }
  1698.  
  1699.  
  1700. /*
  1701.  * Write out an integer, in the specified radix. Update the physical cursor
  1702.  * position. This will not handle any negative numbers; maybe it should.
  1703.  */
  1704. mlputi(i, r)
  1705. int i, r;
  1706. {
  1707.     register int q;
  1708.     static char hexdigits[] = "0123456789ABCDEF";
  1709.  
  1710.     if (i < 0){
  1711.         i = -i;
  1712.     pputc('-', 1);
  1713.     }
  1714.  
  1715.     q = i/r;
  1716.  
  1717.     if (q != 0)
  1718.       mlputi(q, r);
  1719.  
  1720.     pputc(hexdigits[i%r], 1);
  1721. }
  1722.  
  1723.  
  1724. /*
  1725.  * do the same except as a long integer.
  1726.  */
  1727. mlputli(l, r)
  1728. long l;
  1729. int  r;
  1730. {
  1731.     register long q;
  1732.  
  1733.     if (l < 0){
  1734.         l = -l;
  1735.         pputc('-', 1);
  1736.     }
  1737.  
  1738.     q = l/r;
  1739.  
  1740.     if (q != 0)
  1741.       mlputli(q, r);
  1742.  
  1743.     pputc((int)(l%r)+'0', 1);
  1744. }
  1745.  
  1746.  
  1747. /*
  1748.  * scrolldown - use stuff to efficiently move blocks of text on the
  1749.  *              display, and update the pscreen array to reflect those
  1750.  *              moves...
  1751.  *
  1752.  *        wp is the window to move in
  1753.  *        r  is the row at which to begin scrolling
  1754.  *        n  is the number of lines to scrol
  1755.  */
  1756. scrolldown(wp, r, n)
  1757. WINDOW *wp;
  1758. int     r, n;
  1759. {
  1760. #ifdef    TERMCAP
  1761.     register int i;
  1762.     register int l;
  1763.     register VIDEO *vp1;
  1764.     register VIDEO *vp2;
  1765.  
  1766.     if(!n)
  1767.       return;
  1768.  
  1769.     if(r < 0){
  1770.     r = wp->w_toprow;
  1771.     l = wp->w_ntrows;
  1772.     }
  1773.     else{
  1774.     if(r > wp->w_toprow)
  1775.         vscreen[r-1]->v_flag |= VFCHG;
  1776.     l = wp->w_toprow+wp->w_ntrows-r;
  1777.     }
  1778.  
  1779.     o_scrolldown(r, n);
  1780.  
  1781.     for(i=l-n-1; i >=  0; i--){
  1782.     vp1 = pscreen[r+i]; 
  1783.     vp2 = pscreen[r+i+n];
  1784.     bcopy(vp1, vp2, term.t_ncol * sizeof(CELL));
  1785.     }
  1786.     pprints(r+n-1, r);
  1787.     ttrow = HUGE;
  1788.     ttcol = HUGE;
  1789. #endif /* TERMCAP */
  1790. }
  1791.  
  1792.  
  1793. /*
  1794.  * scrollup - use tcap stuff to efficiently move blocks of text on the
  1795.  *            display, and update the pscreen array to reflect those
  1796.  *            moves...
  1797.  */
  1798. scrollup(wp, r, n)
  1799. WINDOW *wp;
  1800. int     r, n;
  1801. {
  1802. #ifdef    TERMCAP
  1803.     register int i;
  1804.     register VIDEO *vp1;
  1805.     register VIDEO *vp2;
  1806.  
  1807.     if(!n)
  1808.       return;
  1809.  
  1810.     if(r < 0)
  1811.       r = wp->w_toprow;
  1812.  
  1813.     o_scrollup(r, n);
  1814.  
  1815.     i = 0;
  1816.     while(1){
  1817.     if(Pmaster){
  1818.         if(!(r+i+n < wp->w_toprow+wp->w_ntrows))
  1819.           break;
  1820.     }
  1821.     else{
  1822.         if(!((i < wp->w_ntrows-n)&&(r+i+n < wp->w_toprow+wp->w_ntrows)))
  1823.           break;
  1824.     }
  1825.     vp1 = pscreen[r+i+n]; 
  1826.     vp2 = pscreen[r+i];
  1827.     bcopy(vp1, vp2, term.t_ncol * sizeof(CELL));
  1828.     i++;
  1829.     }
  1830.     pprints(wp->w_toprow+wp->w_ntrows-n, wp->w_toprow+wp->w_ntrows-1);
  1831.     ttrow = HUGE;
  1832.     ttcol = HUGE;
  1833. #endif /* TERMCAP */
  1834. }
  1835.  
  1836.  
  1837. /*
  1838.  * print spaces in the physical screen starting from row abs(n) working in
  1839.  * either the positive or negative direction (depending on sign of n).
  1840.  */
  1841. pprints(x, y)
  1842. int x, y;
  1843. {
  1844.     register int i;
  1845.     register int j;
  1846.  
  1847.     if(x < y){
  1848.     for(i = x;i <= y; ++i){
  1849.         for(j = 0; j < term.t_ncol; j++){
  1850.         pscreen[i]->v_text[j].c = ' ';
  1851.         pscreen[i]->v_text[j].a = 0;
  1852.         }
  1853.         }
  1854.     }
  1855.     else{
  1856.     for(i = x;i >= y; --i){
  1857.         for(j = 0; j < term.t_ncol; j++){
  1858.         pscreen[i]->v_text[j].c = ' ';
  1859.         pscreen[i]->v_text[j].a = 0;
  1860.         }
  1861.         }
  1862.     }
  1863.     ttrow = y;
  1864.     ttcol = 0;
  1865. }
  1866.  
  1867.  
  1868.  
  1869. /*
  1870.  * doton - return the physical line number that the dot is on in the
  1871.  *         current window, and by side effect the number of lines remaining
  1872.  */
  1873. doton(r, chs)
  1874. int       *r;
  1875. unsigned  *chs;
  1876. {
  1877.     register int  i = 0;
  1878.     register LINE *lp = curwp->w_linep;
  1879.     int      l = -1;
  1880.  
  1881.     *chs = 0;
  1882.     while(i++ < curwp->w_ntrows){
  1883.     if(lp == curwp->w_dotp)
  1884.       l = i-1;
  1885.     lp = lforw(lp);
  1886.     if(lp == curwp->w_bufp->b_linep){
  1887.         i++;
  1888.         break;
  1889.     }
  1890.     if(l >= 0)
  1891.       (*chs) += llength(lp);
  1892.     }
  1893.     *r = i - l - term.t_mrow;
  1894.     return(l+curwp->w_toprow);
  1895. }
  1896.  
  1897.  
  1898.  
  1899. /*
  1900.  * resize_pico - given new window dimensions, allocate new resources
  1901.  */
  1902. resize_pico(row, col)
  1903. int  row, col;
  1904. {
  1905.     int old_nrow, old_ncol;
  1906.     register int i;
  1907.     register VIDEO *vp;
  1908.  
  1909.     old_nrow = term.t_nrow;
  1910.     old_ncol = term.t_ncol;
  1911.  
  1912.     term.t_nrow = row;
  1913.     term.t_ncol = col;
  1914.  
  1915.     if (old_ncol == term.t_ncol && old_nrow == term.t_nrow)
  1916.       return(TRUE);
  1917.  
  1918.     if(curwp){
  1919.     curwp->w_toprow = 2;
  1920.     curwp->w_ntrows = term.t_nrow - curwp->w_toprow - term.t_mrow;
  1921.     }
  1922.  
  1923.     if(Pmaster){
  1924.     fillcol = Pmaster->fillcolumn;
  1925.     (*Pmaster->resize)();
  1926.     }
  1927.     else if(userfillcol > 0)
  1928.       fillcol = userfillcol;
  1929.     else
  1930.       fillcol = term.t_ncol - 6;           /* we control the fill column */
  1931.  
  1932.     /* 
  1933.      * free unused screen space ...
  1934.      */
  1935.     for(i=term.t_nrow+1; i <= old_nrow; ++i){
  1936.     free((char *) vscreen[i]);
  1937.     free((char *) pscreen[i]);
  1938.     }
  1939.  
  1940.     /* 
  1941.      * realloc new space for screen ...
  1942.      */
  1943.     if((vscreen=(VIDEO **)realloc(vscreen,(term.t_nrow+1)*sizeof(VIDEO *))) == NULL){
  1944.     if(Pmaster)
  1945.       return(-1);
  1946.     else
  1947.       exit(1);
  1948.     }
  1949.  
  1950.     if((pscreen=(VIDEO **)realloc(pscreen,(term.t_nrow+1)*sizeof(VIDEO *))) == NULL){
  1951.     if(Pmaster)
  1952.       return(-1);
  1953.     else
  1954.       exit(1);
  1955.     }
  1956.  
  1957.     for (i = 0; i <= term.t_nrow; ++i) {
  1958.     if(i <= old_nrow)
  1959.       vp = (VIDEO *) realloc(vscreen[i], sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
  1960.     else
  1961.       vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
  1962.  
  1963.     if (vp == NULL)
  1964.       exit(1);
  1965.     vp->v_flag = VFCHG;
  1966.     vscreen[i] = vp;
  1967.     if(old_ncol < term.t_ncol){  /* don't let any garbage in */
  1968.         vtrow = i;
  1969.         vtcol = (i < old_nrow) ? old_ncol : 0;
  1970.         vteeol();
  1971.     }
  1972.  
  1973.     if(i <= old_nrow)
  1974.       vp = (VIDEO *) realloc(pscreen[i], sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
  1975.     else
  1976.       vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
  1977.  
  1978.     if (vp == NULL)
  1979.       exit(1);
  1980.  
  1981.     vp->v_flag = VFCHG;
  1982.     pscreen[i] = vp;
  1983.     }
  1984.  
  1985.     if(!ResizeBrowser()){
  1986.     if(Pmaster && Pmaster->headents){
  1987.         ResizeHeader();
  1988.     }
  1989.     else{
  1990.         lchange(WFHARD);                   /* set update flags... */
  1991.         curwp->w_flag |= WFMODE;           /* and modeline so we  */
  1992.         refresh(0, 1);                     /* redraw the whole enchilada. */
  1993.         update();                          /* do it */
  1994.     }
  1995.     }
  1996.  
  1997.     return(TRUE);
  1998. }
  1999.  
  2000.  
  2001. /*
  2002.  * showCompTitle - display the anchor line passed in from pine
  2003.  */
  2004. showCompTitle()
  2005. {
  2006.     if(Pmaster){
  2007.     register char *bufp;
  2008.     extern   char *pico_anchor;
  2009.  
  2010.     if((bufp = pico_anchor) == NULL)
  2011.       return(1);
  2012.  
  2013.     movecursor(COMPOSER_TITLE_LINE, 0);
  2014.     (*term.t_rev)(1);   
  2015.     while (ttcol < term.t_ncol)
  2016.       if(*bufp != '\0')
  2017.         pputc(*bufp++, 1);
  2018.           else
  2019.         pputc(' ', 1);
  2020.  
  2021.     (*term.t_rev)(0);
  2022.     movecursor(COMPOSER_TITLE_LINE + 1, 0);
  2023.     peeol();
  2024.     }
  2025. }
  2026.  
  2027.  
  2028.  
  2029. /*
  2030.  * zotdisplay - blast malloc'd space created for display maps
  2031.  */
  2032. zotdisplay()
  2033. {
  2034.     register int i;
  2035.  
  2036.     for (i = 0; i <= term.t_nrow; ++i){        /* free screens */
  2037.     free((char *) vscreen[i]);
  2038.     free((char *) pscreen[i]);
  2039.     }
  2040.  
  2041.     free((char *) vscreen);
  2042.     free((char *) pscreen);
  2043. }
  2044.  
  2045.  
  2046.  
  2047. /*
  2048.  * nlforw() - returns the number of lines from the top to the dot
  2049.  */
  2050. nlforw()
  2051. {
  2052.     register int  i = 0;
  2053.     register LINE *lp = curwp->w_linep;
  2054.     
  2055.     while(lp != curwp->w_dotp){
  2056.     lp = lforw(lp);
  2057.     i++;
  2058.     }
  2059.     return(i);
  2060. }
  2061.  
  2062.  
  2063.  
  2064. /*
  2065.  * pputc - output the given char, keep track of it on the physical screen
  2066.  *       array, and keep track of the cursor
  2067.  */
  2068. pputc(c, a)
  2069. int   c;                /* char to write */
  2070. int   a;                /* and its attribute */
  2071. {
  2072.     if((ttcol >= 0 && ttcol < term.t_ncol) 
  2073.        && (ttrow >= 0 && ttrow <= term.t_nrow)){
  2074. /*    (*term.t_rev)(a);*/
  2075.     (*term.t_putchar)(c);            /* write it */
  2076. /*    (*term.t_rev)(!a);*/
  2077.     pscreen[ttrow]->v_text[ttcol].c = c;    /* keep track of it */
  2078.     pscreen[ttrow]->v_text[ttcol++].a = a;    /* keep track of it */
  2079.     }
  2080. }
  2081.  
  2082.  
  2083. /*
  2084.  * pputs - print a string and keep track of the cursor
  2085.  */
  2086. pputs(s, a)
  2087. register char *s;            /* string to write */
  2088. register int   a;            /* and its attribute */
  2089. {
  2090.     while (*s != '\0')
  2091.       pputc(*s++, a);
  2092. }
  2093.  
  2094.  
  2095. /*
  2096.  * peeol - physical screen array erase to end of the line.  remember to
  2097.  *       track the cursor.
  2098.  */
  2099. peeol()
  2100. {
  2101.     register int r = ttrow;
  2102.     register int c = ttcol;
  2103.     CELL         cl;
  2104.  
  2105.     cl.c = ' ';
  2106.     cl.a = 0;
  2107.     (*term.t_eeol)();
  2108.     while(c < term.t_ncol && c >= 0 && r <= term.t_nrow && r >= 0)
  2109.       pscreen[r]->v_text[c++] = cl;
  2110. }
  2111.  
  2112.  
  2113. /*
  2114.  * pscr - return the character cell on the physical screen map on the 
  2115.  *        given line, l, and offset, o.
  2116.  */
  2117. CELL *
  2118. pscr(l, o)
  2119. int l, o;
  2120. {
  2121.     if((l >= 0 && l <= term.t_nrow) && (o >= 0 && o < term.t_ncol))
  2122.       return(&(pscreen[l]->v_text[o]));
  2123.     else
  2124.       return(NULL);
  2125. }
  2126.  
  2127.  
  2128. /*
  2129.  * pclear() - clear the physical screen from row x through row y
  2130.  */
  2131. pclear(x, y)
  2132. register int x;
  2133. register int y;
  2134. {
  2135.     register int i;
  2136.  
  2137.     for(i=x; i < y; i++){
  2138.     movecursor(i, 0);
  2139.     peeol();
  2140.     }
  2141. }
  2142.  
  2143.  
  2144. /*
  2145.  * dumbroot - just get close 
  2146.  */
  2147. dumbroot(x, b)
  2148. int x, b;
  2149. {
  2150.     if(x < b)
  2151.       return(1);
  2152.     else
  2153.       return(dumbroot(x/b, b) + 1);
  2154. }
  2155.  
  2156.  
  2157. /*
  2158.  * dumblroot - just get close 
  2159.  */
  2160. dumblroot(x, b)
  2161. long x;
  2162. int  b;
  2163. {
  2164.     if(x < b)
  2165.       return(1);
  2166.     else
  2167.       return(dumblroot(x/b, b) + 1);
  2168. }
  2169.  
  2170.  
  2171. /*
  2172.  * pinsertc - use optimized insert, fixing physical screen map.
  2173.  *            returns true if char written, false otherwise
  2174.  */
  2175. pinsert(c)
  2176. CELL c;
  2177. {
  2178.     register int   i;
  2179.     register CELL *p;
  2180.  
  2181.     if(o_insert((char)c.c)){        /* if we've got it, use it! */
  2182.     p = pscreen[ttrow]->v_text;    /* then clean up physical screen */
  2183.     for(i = term.t_ncol-1; i > ttcol; i--)
  2184.       p[i] = p[i-1];        /* shift right */
  2185.     p[ttcol++] = c;            /* insert new char */
  2186.     
  2187.     return(1);
  2188.     }
  2189.  
  2190.     return(0);
  2191. }
  2192.  
  2193.  
  2194. /*
  2195.  * pdel - use optimized delete to rub out the current char and
  2196.  *        fix the physical screen array.
  2197.  *        returns true if optimized the delete, false otherwise
  2198.  */
  2199. pdel()
  2200. {
  2201.     register int   i;
  2202.     register CELL *c;
  2203.  
  2204.     if(delchar){            /* if we've got it, use it! */
  2205.     (*term.t_putchar)('\b');     /* move left a char */
  2206.     --ttcol;
  2207.     o_delete();            /* and delete it */
  2208.  
  2209.     c = pscreen[ttrow]->v_text;    /* then clean up physical screen */
  2210.     for(i=ttcol; i < term.t_ncol; i++)
  2211.       c[i] = c[i+1];
  2212.     c[i].c = ' ';
  2213.     c[i].a = 0;
  2214.     
  2215.     return(1);
  2216.     }
  2217.  
  2218.     return(0);
  2219. }
  2220.  
  2221.  
  2222.  
  2223. /*
  2224.  * wstripe - write out the given string at the given location, and reverse
  2225.  *           video on flagged characters.  Does the same thing as pine's
  2226.  *           stripe.
  2227.  */
  2228. void
  2229. wstripe(line, column, pmt, key)
  2230. int    line, column;
  2231. char    *pmt;
  2232. int      key;
  2233. {
  2234.     register char *buf;
  2235.     register int  i = 0;
  2236.     register int  j = 0;
  2237.     register int  l;
  2238.  
  2239.     l = strlen(pmt);
  2240.     while(1){
  2241.     if(i >= term.t_ncol || j >= l)
  2242.       return;                /* equal strings */
  2243.  
  2244.     if(pmt[j] == key)
  2245.       j++;
  2246.  
  2247.     if(pscr(line, i)->c != pmt[j]){
  2248.         if(j >= 1 && pmt[j-1] == key)
  2249.           j--;
  2250.         break;
  2251.     }
  2252.  
  2253.     j++;
  2254.     i++;
  2255.     }
  2256.  
  2257.     movecursor(line, column+i);
  2258.     buf = &pmt[j];
  2259.     do{
  2260.     if(*buf == key){
  2261.         buf++;
  2262.         (*term.t_rev)(1);
  2263.         pputc(*buf, 1);
  2264.         (*term.t_rev)(0);
  2265.     }
  2266.     else{
  2267.         pputc(*buf, 0);
  2268.     }
  2269.     }    
  2270.     while(*++buf != '\0');
  2271.     peeol();
  2272.     (*term.t_flush)();
  2273. }
  2274.  
  2275.  
  2276.  
  2277. /*
  2278.  *  wkeyhelp - paint list of possible commands on the bottom
  2279.  *             of the display (yet another pine clone)
  2280.  *  NOTE: function key mode is handled here since all the labels
  2281.  *        are the same...
  2282.  */
  2283. wkeyhelp(keymenu)
  2284. KEYMENU *keymenu;
  2285. {
  2286.     char *obufp, *p, fkey[4], linebuf[NLINE];
  2287.     int   row, slot, tspace, nspace[6], index, n;
  2288. #ifdef    MOUSE
  2289.     char  nbuf[NLINE];
  2290. #endif
  2291.  
  2292. #ifdef _WINDOWS
  2293.     pico_config_menu_items (keymenu);
  2294. #endif
  2295.  
  2296.     if(term.t_mrow == 0)
  2297.       return(FALSE);
  2298.  
  2299.     /*
  2300.      * Calculate amount of space for the names column by column...
  2301.      */
  2302.     for(index = 0; index < 6; index++)
  2303.       if(!(gmode&MDFKEY)){
  2304.       nspace[index] = (keymenu[index].name)
  2305.                 ? strlen(keymenu[index].name) : 0;
  2306.       if(keymenu[index+6].name 
  2307.          && (n = strlen(keymenu[index+6].name)) > nspace[index])
  2308.         nspace[index] = n;
  2309.  
  2310.       nspace[index]++;
  2311.       }
  2312.       else
  2313.     nspace[index] = (index < 4) ? 3 : 4;
  2314.  
  2315.     tspace = term.t_ncol/6;        /* total space for each item */
  2316.     index  = 0;
  2317.     for(row = 0; row <= 1; row++){
  2318.     linebuf[0] = '\0';
  2319.     obufp = &linebuf[0];
  2320.     for(slot = 0; slot < 6; slot++){
  2321.         if(keymenu[index].name && keymenu[index].label){
  2322.         if(gmode&MDFKEY){
  2323.             p = fkey;
  2324.             sprintf(fkey, "F%d", (2 * slot) + row + 1);
  2325.         }
  2326.         else
  2327.           p = keymenu[index].name;
  2328. #ifdef    MOUSE
  2329.         sprintf(nbuf, "%.*s %s", nspace[slot], p,
  2330.             keymenu[index].label);
  2331.         register_key(index,
  2332.                  (gmode&MDFKEY) ? F1 + (2 * slot) + row:
  2333.                  (keymenu[index].name[0] == '^')
  2334.                    ? (CTRL | keymenu[index].name[1])
  2335.                    : (keymenu[index].name[0] == 'S'
  2336.                   && !strcmp(keymenu[index].name, "Spc"))
  2337.                    ? ' '
  2338.                    : keymenu[index].name[0],
  2339.                  nbuf, invert_label,
  2340.                  term.t_nrow - 1 + row, (slot * tspace),
  2341.                  strlen(nbuf));
  2342. #endif
  2343.  
  2344.         n = nspace[slot];
  2345.         while(p && *p && n--){
  2346.             *obufp++ = '~';    /* insert "invert" token */
  2347.             *obufp++ = *p++;
  2348.         }
  2349.  
  2350.         while(n-- > 0)
  2351.           *obufp++ = ' ';
  2352.  
  2353.         p = keymenu[index].label;
  2354.         n = tspace - nspace[slot];
  2355.         while(p && *p && n-- > 0)
  2356.           *obufp++ = *p++;
  2357.  
  2358.         while(n-- > 0)
  2359.           *obufp++ = ' ';
  2360.         }
  2361.         else{
  2362.         n = tspace;
  2363.         while(n--)
  2364.           *obufp++ = ' ';
  2365.  
  2366. #ifdef    MOUSE
  2367.         register_key(index, NODATA, "", NULL, 0, 0, 0);
  2368. #endif
  2369.         }
  2370.  
  2371.         *obufp = '\0';
  2372.         index++;
  2373.     }
  2374.  
  2375.     wstripe(term.t_nrow - 1 + row, 0, linebuf, '~');
  2376.     }
  2377. }
  2378.